ECMA-335 is an international standard that defines a Common Language Infrastructure (CLI) in which applications written in multiple high-level languages can be executed in different system environments without the need to rewrite those applications to take into consideration the unique characteristics of those environments. Unfortunately, malicious applications running un-trusted code (e.g., downloaded applications from the Internet) can present potential risks to local secured computer resources. Code Access Security (CAS) is a well known security mechanism, implemented on computers with an ECMA-335 virtual machine (VM), to limit what managed code can do and access on the computer. In CAS permissions are granted, or refused, based on code evidences and security policies. Resources (e.g., assemblies) can specify requested permissions by using declarative security, which uses attributes to place security information into the metadata of the resource. At runtime if the code attempts to access any resource which requires permissions as specified by declarative security, a demand is issued to determine whether the code calling the resource and all other codes that have been called up that point (call stack) satisfies the requested permissions. If so, access to the resource is allowed, otherwise a security exception is thrown. CAS enforces this by doing “stack walks” where every method on the call stack (e.g., the chain of code being executed) is evaluated for the demanded permission. Although time consuming, this prevents luring attacks by less trusted code that can sit between a trusted application (bottom of the stack) and the code accessing the resource (top of the stack).
A call stack is divided up into contiguous pieces called stack frames, or frames for short; each frame is the data associated with one call to one function, also known as “method”. When a demand for permission is executed CAS starts a stack walk for every frame in the execution stack, starting with the current caller method. Each stack frame, as well as assembly and application domain transitions, are evaluated to see if they satisfy the condition required by the permission.
Executing stack walks is a heavyweight process, but it does ensure that every caller (e.g., method call) has access to a resource before executing the code. However in some cases, like iterations or recursion, having multiple stack walks for the same set of permissions and the same call stalk (e.g. iterations), or a superset of the stack (e.g. recursion), does not augment security but greatly impacts application performance.
For such conditions, CAS includes a mechanism called an assertion, which allows the reduction of permissions checks up to a point in the stack walk where permissions do not need to be verified. For example, if a frame in a call stack asserts a permission to read files, then this permission is not checked for the frames above the frame that made the assertion. It can be safely assumed that the asserted permission has been determined to be allowed for all the above frames. Assertions, even if indirectly, are time savers because they reduce the number of stack frames to process. Using asserts for non-security purposes like performance optimizations, however, can be potentially harmful because they reduce the number of security checks done at runtime. Assertions require careful and manual auditing and any error (e.g. demand for permissions not executed) can lead to security vulnerabilities in applications.
FIG. 1 is block diagram of a security assertion in normal operation. By way of illustration, Assembly A may be an application from the Internet which calls Assembly B. Assembly B may be local code from a base class library. If a method inside Assembly B asserts a permission, a stack walk is prevented from proceeding up the call stack beyond the code that asserted the permission, which in this case is Method B1. If a demand is later made for the same permission(s), the call stack is checked once again, up to the point where the permission was previously asserted, and no further. Thus, in prior art systems even if code higher on the call stack (Assembly A) does not have the requisite permission(s) to access the resource, they can still access it because the stack walk is stopped before reaching that stack frame (or application domain/assembly transition). Since assertions remove security requirements, an incorrectly used assertion has the potential of opening up security vulnerabilities.
Problems of the present implementations are that most stack walks are invisible or unknown to the programmer; hence any performance problem is hard to diagnose. Using manual assertions is difficult and potentially harmful (if misused) making this mechanism a dangerous one to solve performance issues. As such, there is a need for safe performance optimizations that does not have the drawbacks of current implementations.